package stat

import (
	"sync"
	"time"

	"github.com/zeromicro/go-zero/core/collection"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/kv"
)

const (
	slots = 300
)

type (
	Stat struct {
		store       kv.Store
		timingWheel *collection.TimingWheel
		lock        sync.Mutex
	}
)

func NewStat(c cache.ClusterConf) (*Stat, error) {
	stat := &Stat{
		store: kv.NewStore(c),
	}

	timingWheel, err := collection.NewTimingWheel(time.Second, slots, func(k, v any) {
		key, ok := k.(string)
		if !ok {
			return
		}
		stat.DelValue(key, v)
	})
	if err != nil {
		return nil, err
	}

	stat.timingWheel = timingWheel

	return stat, nil
}

func (s *Stat) DelValue(key string, v any) {
	s.lock.Lock()
	s.store.Srem(key, v)
	s.lock.Unlock()
}

func (s *Stat) SaddWithExpire(key string, v any, seconds uint32) {
	s.lock.Lock()
	n, _ := s.store.Sadd(key, v)
	s.lock.Unlock()
	if seconds == 0 {
		return
	}
	delay := time.Duration(seconds) * time.Second
	if n > 0 {
		s.timingWheel.SetTimer(key, interface{}(v), delay)
	} else {
		s.timingWheel.MoveTimer(key, delay)
	}
}

func (s *Stat) Sadd(key string, v any) {
	s.lock.Lock()
	s.store.Sadd(key, v)
	s.lock.Unlock()
}

func (s *Stat) DelSets(key string) {
	s.lock.Lock()
	s.store.Del(key)
	s.lock.Unlock()
}

func (s *Stat) IsMember(key string, v any) bool {
	b, err := s.store.Sismember(key, v)
	if err != nil {
		return false
	}
	return b
}

func (s *Stat) GetMemberCount(key string) uint32 {
	c, err := s.store.Scard(key)
	if err != nil {
		return 0
	}

	return uint32(c)
}

func (s *Stat) KvSet(key, value string) error {
	return s.store.Set(key, value)
}

func (s *Stat) KvGet(key string) (string, error) {
	return s.store.Get(key)
}
